package com.framework.loippi.service.impl;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import javax.annotation.Resource;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.ImageWriter;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.plugins.jpeg.JPEGImageWriteParam;
import javax.imageio.stream.ImageOutputStream;
import javax.servlet.ServletContext;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.CompareToBuilder;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.web.context.ServletContextAware;
import org.springframework.web.multipart.MultipartFile;
import org.w3c.dom.Element;

import com.framework.loippi.service.FileService;
import com.framework.loippi.service.TUserSettingService;
import com.framework.loippi.support.FileInfo;
import com.framework.loippi.support.FileInfo.FileType;
import com.framework.loippi.support.FileInfo.OrderType;
import com.framework.loippi.support.Setting;
import com.framework.loippi.utils.FreemarkerUtils;

/**
 * Service - 文件
 *
 * @author Mounate Yan。
 * @version 1.0
 */
@Service("fileServiceImpl")
public class FileServiceImpl implements FileService, ServletContextAware {

    /**
     * servletContext
     */
    private ServletContext servletContext;

    @Resource
    private TUserSettingService tUserSettingService;

	private TaskExecutor taskExecutor;

	public void setServletContext(ServletContext servletContext) {
		this.servletContext = servletContext;
	}



    public boolean isValid(FileType fileType, MultipartFile multipartFile) {
        if (multipartFile == null) {
            return false;
        }
        Setting setting = tUserSettingService.get();
        if (setting.getUploadMaxSize() != null && setting.getUploadMaxSize() != 0 && multipartFile.getSize() > setting.getUploadMaxSize() * 1024L * 1024L) {
            return false;
        }
        String[] uploadExtensions;
        if (fileType == FileType.flash) {
            uploadExtensions = setting.getUploadFlashExtensions();
        } else if (fileType == FileType.media) {
            uploadExtensions = setting.getUploadMediaExtensions();
        } else if (fileType == FileType.file) {
            uploadExtensions = setting.getUploadFileExtensions();
        } else {
            uploadExtensions = setting.getUploadImageExtensions();
        }
        if (ArrayUtils.isNotEmpty(uploadExtensions)) {
            return FilenameUtils.isExtension(multipartFile.getOriginalFilename(), uploadExtensions);
        }
        return false;
    }



    public String uploadLocal(FileType fileType, MultipartFile multipartFile) {
        if (multipartFile == null) {
            return null;
        }
        Setting setting = tUserSettingService.get();
        String uploadPath;
        if (fileType == FileType.flash) {
            uploadPath = setting.getFlashUploadPath();
        } else if (fileType == FileType.media) {
            uploadPath = setting.getMediaUploadPath();
        } else if (fileType == FileType.file) {
            uploadPath = setting.getFileUploadPath();
        } else {
            uploadPath = setting.getImageUploadPath();
        }
        try {
            Map<String, Object> model = new HashMap<String, Object>();
            model.put("uuid", UUID.randomUUID().toString());
            String path = FreemarkerUtils.process(uploadPath, model);
            String destPath = path + UUID.randomUUID() + "." + FilenameUtils.getExtension(multipartFile.getOriginalFilename());
            File destFile = new File(servletContext.getRealPath(destPath));
            if (!destFile.getParentFile().exists()) {
                destFile.getParentFile().mkdirs();
            }
            multipartFile.transferTo(destFile);
            return destPath;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public List<FileInfo> browser(String path, FileType fileType, OrderType orderType) {
        if (path != null) {
            if (!path.startsWith("/")) {
                path = "/" + path;
            }
            if (!path.endsWith("/")) {
                path += "/";
            }
        } else {
            path = "/";
        }
        Setting setting = tUserSettingService.get();
        String uploadPath;
        if (fileType == FileType.flash) {
            uploadPath = setting.getFlashUploadPath();
        } else if (fileType == FileType.media) {
            uploadPath = setting.getMediaUploadPath();
        } else if (fileType == FileType.file) {
            uploadPath = setting.getFileUploadPath();
        } else {
            uploadPath = setting.getImageUploadPath();
        }
        String browsePath = StringUtils.substringBefore(uploadPath, "${");
        browsePath = StringUtils.substringBeforeLast(browsePath, "/") + path;

        List<FileInfo> fileInfos = new ArrayList<FileInfo>();
        if (browsePath.indexOf("..") >= 0) {
            return fileInfos;
        }
        fileInfos = browser(browsePath);
        if (orderType == OrderType.size) {
            Collections.sort(fileInfos, new SizeComparator());
        } else if (orderType == OrderType.type) {
            Collections.sort(fileInfos, new TypeComparator());
        } else {
            Collections.sort(fileInfos, new NameComparator());
        }
        return fileInfos;
    }

    public String uploadthumbnail(String raw,int w, int h, float compressionQuality) {
        if(StringUtils.isEmpty(raw)){
            return null;
        }
        Setting setting = tUserSettingService.get();
        String uploadPath = setting.getImageUploadPath();

        Image src = null;
        try {
            File file = new File(servletContext.getRealPath(raw));
            src = javax.imageio.ImageIO.read(file); // 构造Image对象
            Map<String, Object> model = new HashMap<String, Object>();
            model.put("uuid", UUID.randomUUID().toString());
            String path = FreemarkerUtils.process(uploadPath, model);
            String destPath = path + UUID.randomUUID() + "."
                    + FilenameUtils.getExtension(file.getName());
            String readPath = servletContext.getRealPath(destPath);
            int old_w = src.getWidth(null); // 得到源图宽
            int old_h = src.getHeight(null);
            int new_w = 0;
            int new_h = 0; // 得到源图长

            double w2 = (old_w * 1.00) / (w * 1.00);
            double h2 = (old_h * 1.00) / (h * 1.00);

            // 图片跟据长宽留白，成一个正方形图。
            BufferedImage oldpic;
            if (old_w > old_h) {
                oldpic = new BufferedImage(old_w, old_w,
                        BufferedImage.TYPE_INT_RGB);
            } else {
                if (old_w < old_h) {
                    oldpic = new BufferedImage(old_h, old_h,
                            BufferedImage.TYPE_INT_RGB);
                } else {
                    oldpic = new BufferedImage(old_w, old_h,
                            BufferedImage.TYPE_INT_RGB);
                }
            }
            Graphics2D g = oldpic.createGraphics();
            g.setColor(Color.white);
            if (old_w > old_h) {
                g.fillRect(0, 0, old_w, old_w);
                g.drawImage(src, 0, (old_w - old_h) / 2, old_w, old_h,
                        Color.white, null);
            } else {
                if (old_w < old_h) {
                    g.fillRect(0, 0, old_h, old_h);
                    g.drawImage(src, (old_h - old_w) / 2, 0, old_w, old_h,
                            Color.white, null);
                } else {
                    // g.fillRect(0,0,old_h,old_h);
                    g.drawImage(src.getScaledInstance(old_w, old_h,
                            Image.SCALE_SMOOTH), 0, 0, null);
                }
            }
            g.dispose();
            src = oldpic;
            // 图片调整为方形结束
            if (old_w > w) {
                new_w = (int) Math.round(old_w / w2);
            }
            else {
                new_w = old_w;
            }
            if (old_h > h) {
                new_h = (int) Math.round(old_h / h2);// 计算新图长宽
            }
            else {
                new_h = old_h;
            }
            BufferedImage image_to_save = new BufferedImage(new_w, new_h,
                    BufferedImage.TYPE_INT_RGB);
            image_to_save.getGraphics().drawImage(
                    src.getScaledInstance(new_w, new_h, Image.SCALE_SMOOTH), 0,
                    0, null);
            FileOutputStream fos = new FileOutputStream(readPath); // 输出到文件流

            // 旧的使用 jpeg classes进行处理的方法
            // JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(fos);
            // JPEGEncodeParam jep =
            // JPEGCodec.getDefaultJPEGEncodeParam(image_to_save);
            /* 压缩质量 */
            // jep.setQuality(per, true);
            // encoder.encode(image_to_save, jep);

            // 新的方法
            saveAsJPEG(100, image_to_save, compressionQuality, fos);

            fos.close();
            return destPath;
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return null;
    }

    /**
     * 以JPEG编码保存图片
     *
     * @param dpi
     *            分辨率
     * @param image_to_save
     *            要处理的图像图片
     * @param JPEGcompression
     *            压缩比
     * @param fos
     *            文件输出流
     * @throws IOException
     */
    public static void saveAsJPEG(Integer dpi, BufferedImage image_to_save,
                                  float JPEGcompression, FileOutputStream fos) throws IOException {

        // useful documentation at
        // http://docs.oracle.com/javase/7/docs/api/javax/imageio/metadata/doc-files/jpeg_metadata.html
        // useful example program at
        // http://johnbokma.com/java/obtaining-image-metadata.html to output
        // JPEG data

        // old jpeg class
        // com.sun.image.codec.jpeg.JPEGImageEncoder jpegEncoder =
        // com.sun.image.codec.jpeg.JPEGCodec.createJPEGEncoder(fos);
        // com.sun.image.codec.jpeg.JPEGEncodeParam jpegEncodeParam =
        // jpegEncoder.getDefaultJPEGEncodeParam(image_to_save);

        // Image writer
        ImageWriter imageWriter = ImageIO.getImageWritersBySuffix("jpg").next();
        ImageOutputStream ios = ImageIO.createImageOutputStream(fos);
        imageWriter.setOutput(ios);
        // and metadata
        IIOMetadata imageMetaData = imageWriter.getDefaultImageMetadata(
                new ImageTypeSpecifier(image_to_save), null);

        if (dpi != null && !dpi.equals("")) {

            // old metadata
            // jpegEncodeParam.setDensityUnit(com.sun.image.codec.jpeg.JPEGEncodeParam.DENSITY_UNIT_DOTS_INCH);
            // jpegEncodeParam.setXDensity(dpi);
            // jpegEncodeParam.setYDensity(dpi);

            // new metadata
            Element tree = (Element) imageMetaData
                    .getAsTree("javax_imageio_jpeg_image_1.0");
            Element jfif = (Element) tree.getElementsByTagName("app0JFIF")
                    .item(0);
            jfif.setAttribute("Xdensity", Integer.toString(dpi));
            jfif.setAttribute("Ydensity", Integer.toString(dpi));

        }

        if (JPEGcompression >= 0 && JPEGcompression <= 1f) {

            // old compression
            // jpegEncodeParam.setQuality(JPEGcompression,false);

            // new Compression
            JPEGImageWriteParam jpegParams = (JPEGImageWriteParam) imageWriter
                    .getDefaultWriteParam();
            jpegParams.setCompressionMode(JPEGImageWriteParam.MODE_EXPLICIT);
            jpegParams.setCompressionQuality(JPEGcompression);

        }

        // old write and clean
        // jpegEncoder.encode(image_to_save, jpegEncodeParam);

        // new Write and clean up
        imageWriter.write(imageMetaData,
                new IIOImage(image_to_save, null, null), null);
        ios.close();
        imageWriter.dispose();

    }

    private class NameComparator implements Comparator<FileInfo> {
        public int compare(FileInfo fileInfos1, FileInfo fileInfos2) {
            return new CompareToBuilder().append(!fileInfos1.getIsDirectory(), !fileInfos2.getIsDirectory()).append(fileInfos1.getName(), fileInfos2.getName()).toComparison();
        }
    }

    private class SizeComparator implements Comparator<FileInfo> {
        public int compare(FileInfo fileInfos1, FileInfo fileInfos2) {
            return new CompareToBuilder().append(!fileInfos1.getIsDirectory(), !fileInfos2.getIsDirectory()).append(fileInfos1.getSize(), fileInfos2.getSize()).toComparison();
        }
    }

    private class TypeComparator implements Comparator<FileInfo> {
        public int compare(FileInfo fileInfos1, FileInfo fileInfos2) {
            return new CompareToBuilder().append(!fileInfos1.getIsDirectory(), !fileInfos2.getIsDirectory()).append(FilenameUtils.getExtension(fileInfos1.getName()), FilenameUtils.getExtension(fileInfos2.getName())).toComparison();
        }
    }

    public void upload(String path, File file, String contentType) {
        File destFile = new File(servletContext.getRealPath(path));
        try {
            FileUtils.moveFile(file, destFile);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public String getUrl(String path) {
        Setting setting = tUserSettingService.get();
        if(path == null || "".equals(path) || path.startsWith("http://")|| "null".equals(path)){
            return path;
        }
        return setting.getSiteUrl() + path;
    }

    public List<FileInfo> browser(String path) {
        Setting setting = tUserSettingService.get();
        List<FileInfo> fileInfos = new ArrayList<FileInfo>();
        File directory = new File(servletContext.getRealPath(path));
        if (directory.exists() && directory.isDirectory()) {
            for (File file : directory.listFiles()) {
                FileInfo fileInfo = new FileInfo();
                fileInfo.setName(file.getName());
                fileInfo.setUrl(setting.getSiteUrl() + path + file.getName());
                fileInfo.setIsDirectory(file.isDirectory());
                fileInfo.setSize(file.length());
                fileInfo.setLastModified(new Date(file.lastModified()));
                fileInfos.add(fileInfo);
            }
        }
        return fileInfos;
    }

}